package com.gralves.elasticsearch.utils.bulk.impl;

import com.gralves.elasticsearch.utils.anno.TimeStatistics;
import com.gralves.elasticsearch.utils.bulk.EsCommonService;
import com.gralves.elasticsearch.utils.config.enumeration.QueryMethod;
import com.gralves.elasticsearch.utils.config.enumeration.QueryType;
import com.gralves.elasticsearch.utils.util.bean.PageQuery;
import com.gralves.elasticsearch.utils.util.bean.QueryParam;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

/**
 * @author zhouyibin
 * @date 2018/12/24
 * @desc
 */
@Slf4j
@Service
@Primary
public class EsCommonServiceImpl implements EsCommonService {

    private final TransportClient transportClient;

    @Autowired
    public EsCommonServiceImpl(TransportClient transportClient) {
        this.transportClient = transportClient;
    }

    @Override
    @TimeStatistics("批量插入文档")
    public BulkResponse documentsInsert(String index, String type, String key, ArrayList<HashMap<String, Object>> entityParams) {
        BulkRequestBuilder requestBuilder = transportClient.prepareBulk();
        entityParams.forEach(params -> {
            log.info(">>>>>>>>>>>>>>>>>>>>>>>[ params : {} ]<<<<<<<<<<<<<<<<<<<<<<", params);
            /*6.3.2版本使用map作为数据源,不在支持使用json字符串*/
            /*requestBuilder.add(transportClient.prepareIndex("customer","customer",String.valueOf(i)).setSource(json));*/
            requestBuilder.add(transportClient.prepareIndex(index, type, String.valueOf(params.get(key))).setSource(params));
        });
        return requestBuilder.execute().actionGet();
    }

    @Override
    @TimeStatistics("插入单条文档")
    public IndexResponse documentInsert(String index, String type, String key, HashMap<String, Object> entityParams) {
        log.info(">>>>>>>>>>>>>>>>>>>>>>>[ params : {} ]<<<<<<<<<<<<<<<<<<<<<<", entityParams);
        /*6.3.2版本使用map作为数据源,不在支持使用json字符串*/
        /*requestBuilder.add(transportClient.prepareIndex("customer","customer",String.valueOf(i)).setSource(json));*/
        return transportClient.prepareIndex(index, type, (String) entityParams.get(key)).setSource(entityParams).get();
    }

    @Override
    @TimeStatistics("批量删除文档")
    public BulkResponse deleteDocuments(String index, String type, ArrayList<String> documentIds) {
        BulkRequestBuilder builder = transportClient.prepareBulk();
        documentIds.forEach(id -> builder.add(transportClient.prepareDelete(index, type, id)));
        return builder.get();
    }

    @Override
    @TimeStatistics("删除单条文档")
    public DeleteResponse deleteDocument(String index, String type, String documentId) {
        return transportClient.prepareDelete(index, type, documentId).get();
    }

    @Override
    @TimeStatistics("根据文档Id查询文档")
    public GetResponse getDocument(String index, String type, String documentId) {
        return transportClient.prepareGet(index, type, documentId).get();
    }

    @Override
    public SearchResponse customQuery(String index, String type, PageQuery pageQuery) {
        String uuid = UUID.randomUUID().toString();
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>[ 执行自定义参数查询 date : {} , SID : {} ]<<<<<<<<<<<<<<<<<<<<<<", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), uuid);
        SearchRequestBuilder searchBuilder = transportClient.prepareSearch(index).setTypes(type);
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        /*判断分页条件是否为空*/
        if (pageQuery.getPageNum() != null && pageQuery.getPageSize() != null) {
            int pageNum = (pageQuery.getPageNum() - 1) * pageQuery.getPageSize();
            int pageSize = pageQuery.getPageSize();
            searchBuilder.setFrom(pageNum);
            searchBuilder.setSize(pageSize);
            log.info(">>>>>>>>>>>>>>>>>>>>>>>>>[ 设置分页参数 当前页: {} , 页大小 : {} , SID : {} ]<<<<<<<<<<<<<<<<<<<<<<", pageNum, pageSize, uuid);
        }
        if (pageQuery.getSortField() != null) {
            if (pageQuery.getSortOrder() != null)
                searchBuilder.addSort(pageQuery.getSortField(), pageQuery.getSortOrder());
                /*默认使用倒序*/
            else searchBuilder.addSort(pageQuery.getSortField(), SortOrder.DESC);
            log.info(">>>>>>>>>>>>>>>>>>>>>>>>>[ 设置排序字段 排序字段: {} , 排序方式 : {} , SID : {} ]<<<<<<<<<<<<<<<<<<<<<<", pageQuery.getSortField(), pageQuery.getSortOrder(), uuid);
        }
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>[ 开始构建查询参数 , SID : {} ]<<<<<<<<<<<<<<<<<<<<<<", uuid);
        Long buildStartTime = System.currentTimeMillis();
        this.assembleArgs(boolQueryBuilder, pageQuery.getQueryParams());
        Long buildCompleteTime = System.currentTimeMillis();
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>[ 构建查询参数完成 耗时 : {} ms , SID : {} ]<<<<<<<<<<<<<<<<<<<<<<", buildCompleteTime - buildStartTime, uuid);
        SearchResponse searchResponse = searchBuilder.setQuery(boolQueryBuilder).get();
        long took = searchResponse.getTook().getMillis();
        long totalHits = searchResponse.getHits().getTotalHits();
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>[ 查询结束 总命中数 : {} , 耗时 : {} ms , SID : {} ]<<<<<<<<<<<<<<<<<<<<<<", totalHits, took, uuid);
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>[ 完成自定义查询 date : {} , SID : {} ]<<<<<<<<<<<<<<<<<<<<<<", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), uuid);
        return searchResponse;
    }

    private void assembleArgs(BoolQueryBuilder boolQueryBuilder, ArrayList<QueryParam> queryParams) {
        ArrayList<String> values = new ArrayList<>(0);
        queryParams.forEach(value -> {
            if (value.getQueryType().getQueryType().equals(5)) values.add(value.getQueryValue());
        });
        for (QueryParam param : queryParams) {
            String queryKey = param.getQueryKey();
            String queryValue = param.getQueryValue();
            QueryMethod queryMethod = param.getQueryMethod();
            QueryType queryType = param.getQueryType();
            switch (queryMethod.getQueryMethod()) {
                case -1:
                    /*must not*/
                    boolQueryBuilder.mustNot(getQueryType(queryType, queryKey, queryValue, values));
                    break;
                case 0:
                    /*must*/
                    boolQueryBuilder.must(getQueryType(queryType, queryKey, queryValue, values));
                    break;
                case 1:
                    /*should*/
                    boolQueryBuilder.should(getQueryType(queryType, queryKey, queryValue, values));
                    break;
            }
        }
    }

    private QueryBuilder getQueryType(QueryType queryType, String queryKey, String queryValue, ArrayList<String> values) {
        switch (queryType.getQueryType()) {
            case 0:
                return QueryBuilders.matchAllQuery();
            case 1:
                return QueryBuilders.fuzzyQuery(queryKey, queryValue);
            case 2:
                return QueryBuilders.matchPhraseQuery(queryKey, queryValue);
            case 3:
                return QueryBuilders.queryStringQuery(queryValue);
            case 4:
                return QueryBuilders.termQuery(queryKey, queryValue);
            case 5:
                return QueryBuilders.termsQuery(queryKey, values);
            case 6:
                return QueryBuilders.wildcardQuery(queryKey, queryValue);
        }
        return QueryBuilders.matchAllQuery();
    }
}
